#include <dirent.h>
#include <string>
#include <functional>
#include <vector>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

// Exception class. Caught in main().
class Error : public std::exception {
  std::string msg_;

public:
  Error() = delete;  // No default constructor.
  explicit Error(const char* msg) : msg_(msg) {}
  explicit Error(std::string&& msg) : msg_(std::move(msg)) {}

  const char* what() const noexcept override {
    return msg_.c_str();
  }
};

// Exception class for system errors that include an errno. Caught in
// main().
class ErrorWithErrno : public Error {
  const int err_;

public:
  ErrorWithErrno() = delete;  // No default constructor.
  explicit ErrorWithErrno(const char* msg) : Error(msg), err_(errno) {}
  explicit ErrorWithErrno(std::string&& msg) : Error(std::move(msg)), err_(errno) {}

  int getErrno() const { return err_; }
};

// This class automatically closes the file descriptor in its destructor.
class AutoCloseFD {
  const int fd_;

public:
  AutoCloseFD() = delete;  // No default constructor.
  explicit AutoCloseFD(const int fd) : fd_(fd) {}
  ~AutoCloseFD();

  int get() const { return fd_; }
};

// This class automatically call munmap in its destructor.
class AutoMunmap {
  void *addr_;
  const size_t size_;

public:
  AutoMunmap() = delete;  // No default constructor.
  AutoMunmap(void *addr, size_t size) :
    addr_(addr), size_(size)
  {}

  // Do an mmap in the constructor.
  AutoMunmap(size_t size, int prot, int flags, int fd, off_t offset);

  ~AutoMunmap();

  void* get() const { return addr_; }
  size_t size() const { return size_; }

  // msync the buffer with the file.
  void sync() const;
};

// Automatically free a pointer that was malloc'ed.
template <typename T>
class AutoFree {
  T* p_;

public:
  AutoFree() = delete; // No default constructor.
  explicit AutoFree(T* p) : p_(p) {}
  ~AutoFree() { free(p_); }

  T* get() const { return p_; }
};

// This class automatically deletes (unlinks) a file in its destructor.  It
// has handy for temporary files that we want to automatically clean up
// when we exit.
class AutoUnlink {
  const int dirfd_;
  const char* filename_;

public:
  AutoUnlink() = delete; // No default constructor.
  AutoUnlink(int dirfd, const char* filename)
    : dirfd_(dirfd), filename_(filename)
  {}
  ~AutoUnlink();
};

// This class creates a file in its constructor and deletes it in its
// destructor. It does not keep a file descriptor open though, so it
// could delete a different file in its destructor if a rename happened
// in the interim.
class AutoCreateAndDeleteFile {
  const int dirfd_;
  const char* filename_;

public:
  AutoCreateAndDeleteFile(
    int dirfd, const char *filename, const char* buf, size_t size, mode_t mode
  );

  // Constructor for more complicated scenarios in which it isn't
  // convenient to provide a string contain the file's contents. Instead
  // the file descriptor is passed to an "init" function.
  AutoCreateAndDeleteFile(
    int dirfd, const char *filename, mode_t mode,
    const std::function<void(int fd)>& initFile
  );

  ~AutoCreateAndDeleteFile();
};

// This class creates an array containing the names of all the files in a
// directory. It does this by running `scandirat` in its constructor.
class ScanDirAt {
  struct dirent **namelist_;
  const int n_;

public:
  explicit ScanDirAt(int fd)
    : n_(scandirat(fd, ".", &namelist_, NULL, alphasort))
  {
    if (n_ < 0) {
      throw ErrorWithErrno("ScanDirAt failed.");
    }
  }

  ~ScanDirAt();

  int size() const { return n_; }

  const char* get(int i) const { return namelist_[i]->d_name; }
};

int create_bind_and_listen_tcp();
uint16_t getportnumber(const int sock);
int add_watch(const int inotify_fd, const char* filename, uint32_t mask);
void createSymlink(const char* target, const int newdirfd, const char* linkname);
int create_file(int dirfd, const char *filename, mode_t mode);
void touch_file(int dirfd, const char *filename, mode_t mode);
void append_file(int dirfd, const char *filename, const char* buf, size_t buflen, mode_t mode);
void write_or_throw(const int fd, const char* buf, size_t buflen);
void create_and_write_file(
  int dirfd, const char *filename, const char* buf, size_t size, mode_t mode
);
void write_repeated_buffer(
  const int fd, const char* msg, size_t msglen, size_t totallen
);
void fd_wait_for_read(const int inotify_fd);
void drain_fd(const int fd);
void kill_and_wait(const pid_t cpid, const int sig);
pid_t fork_child_with_pid(size_t numtries, const std::function<bool(pid_t)>& isDesired);
std::vector<pid_t> search_pids(const char *cmdline, size_t cmdline_len);
pid_t search_pid(const char *cmdline, size_t cmdline_len);
